/**
 * \file PluginObject_Linux.h
 * \brief Plugin declaration class for Linux platforms
 * \author Edouard ROGE
 * \date 16 March 2013
 */
#ifndef _PLUGINOBJECT_LINUX_
#define _PLUGINOBJECT_LINUX_

#include <string>
#include <stdlib.h>
#include <dlfcn.h>
#include <string>
#include "Core/Log/LogManager.h"
#include "Plugin.h"

/**
 * \class PluginObject
 * \brief Base class for every plug-in
 */
template<typename T>
class PluginObject : public Plugin
{
public:

	/**
	 * \fn PluginObject()
	 * \brief Default constructor
	 */
	PluginObject():Plugin(), m_hLibrary(NULL)
	{
	}

	/**
	 * \fn virtual ~PluginObject()
	 * \brief Destructor
	 */
	virtual ~PluginObject()
	{
	}
	
	/**
	 * \fn error Load(const std::string & _strPluginName)
	 * \param _strPluginName name of the plug-in to load
	 * \return an error if occurs
	 * \brief Load a Plugin
	 */
	virtual error Load(const std::string & _strPluginName)
	{
		m_hLibrary = dlopen(_strPluginName.c_str(), RTLD_NOW | RTLD_GLOBAL);
		if ( !m_hLibrary )
		{
			LOG_ERROR("Couldn't find %s plugin %s", _strPluginName.c_str(), dlerror());
			return errFailed;
		}

		LOADINGFUNCTION pLoadingFunc = (LOADINGFUNCTION)::dlsym( m_hLibrary, "CreatePlugin" );
		if ( !pLoadingFunc )
		{
			LOG_ERROR("Couldn't find loading function in %s plugin : %s", _strPluginName.c_str(), dlerror());
			return errFailed;
		}

		m_pInterface = pLoadingFunc();

		if(!m_pInterface)
		{
			Unload();
			LOG_ERROR("Couldn't find interface in  %s plugin", _strPluginName.c_str());
			return errFailed;
		}
		return errOK;
	}

	/**
	 * \fn error Unload()
	 * \return an error if occurs
	 * \brief Unload a Plugin
	 */
	virtual error Unload()
	{
				
		UNLOADINGFUNCTION pUnLoadingFunc = (UNLOADINGFUNCTION)::dlsym( m_hLibrary, "ReleasePlugin" );
		if ( !pUnLoadingFunc )
		{
			LOG_ERROR("Couldn't find unloading function in %s plugin", m_pInterface->GetName().c_str());
			return errFailed;
		}
		
		pUnLoadingFunc((T*)m_pInterface);
		
		m_pInterface = NULL;
		
		dlclose(m_hLibrary);

		return errOK;
	}
	
public:

	/** Handle to the loaded plug-in */
	void * m_hLibrary;

	typedef T* (*LOADINGFUNCTION)();
	
	typedef void (*UNLOADINGFUNCTION)(T*);
};

#endif // _PLUGINOBJECT_LINUX_

